#pragma once

#include "comm/my_queue.h"
#include "comm/my_thread_base.h"
#include "event/my_conn.h"
#include "event/my_event_loop.h"
#include "comm/my_slru.h"
#include "comm/my_str.h"

typedef void (*my_http_conn_free_handler_t)(my_conn_t *conn);

typedef struct {
    int64_t                     max_conns;
    int64_t                     current;
    my_http_conn_free_handler_t free_handler;
    my_queue_t                  head;
} my_http_server_session_pool_t;

static inline void
my_http_server_conn_pool_init(
    my_http_server_session_pool_t *pool,
    int64_t max_conns,
    my_http_conn_free_handler_t free_handler)
{
    if (max_conns <= 0) {
        max_conns = 1;
    }

    pool->max_conns = max_conns;
    pool->free_handler = free_handler;
    my_queue_init(&pool->head);
}

void my_http_server_session_pool_put(my_conn_t *conn, int64_t wait_timeout);

// release all connections.
void my_http_server_conn_pool_release();


////////////////////////////////////////////////////////////////////////////

#define MY_HTTP_CLIENT_SESSION_SLRU_NLIST       2

// the same key may correspond to multiple connections.
// if is_put for both a and b is true, mean current oper is put/delete,
// so we will comparse key str and conn address.
// else, mean current oper is get, so we only comparse key str.
typedef struct {
    my_http_conn_free_handler_t     free_handler;
    my_str_t                        key;
    my_conn_t                       *conn;
    unsigned                        is_put:1;
} my_http_client_conn_data_t;


static inline my_http_client_conn_data_t *
my_http_client_conn_data_create(my_str_t *raw_key, my_conn_t *conn,
    my_http_conn_free_handler_t free_handler, my_bool_t is_put)
{
    my_http_client_conn_data_t *conn_data;
    conn_data = my_thread_calloc((int64_t)sizeof(my_http_client_conn_data_t) + raw_key->len);
    conn_data->free_handler = free_handler;
    conn_data->conn = conn;
    if (is_put) {
        conn_data->is_put = 1;
    }
    conn_data->key.data = (char *)conn_data + sizeof(my_http_client_conn_data_t);
    conn_data->key.len = raw_key->len;
    strncpy(conn_data->key.data, raw_key->data, (size_t)raw_key->len);

    return conn_data;
}


static inline void
my_http_client_conn_data_free(my_http_client_conn_data_t *conn_data)
{
    my_conn_t *conn;

    conn = conn_data->conn;
    conn->data = NULL;

    if (conn_data->free_handler) {
        conn_data->free_handler(conn);
    }

    my_thread_free(conn_data);
}


static inline int
my_http_client_conn_data_cmp(void *key1, void *key2)
{
    int res;

    my_http_client_conn_data_t *a_conn_data = key1;
    my_http_client_conn_data_t *b_conn_data = key2;

    my_str_t *a_key = &a_conn_data->key;
    my_str_t *b_key = &b_conn_data->key;

    // current oper is get, fuzzing matching.
    if (!a_conn_data->is_put || !b_conn_data->is_put) {
        if (a_key->len > b_key->len) {
            return 1;
        }

        if (a_key->len < b_key->len) {
            return -1;
        }

        return strncmp(a_key->data, b_key->data, (size_t)a_key->len);
    }

    // current oper is put or remove, exact matching.
    if (a_key->len > b_key->len) {
        return 1;
    }

    if (a_key->len < b_key->len) {
        return -1;
    }

    res = strncmp(a_key->data, b_key->data, (size_t)a_key->len);
    if (res != 0) {
        return res;
    }

    if (a_conn_data->conn == b_conn_data->conn) {
        return 0;
    }

    return a_conn_data->conn > b_conn_data->conn ? 1 : -1;
}


#define MY_HTTP_CLIENT_SESSION_POOL_LRU_NLIST   2


typedef struct {
    my_http_conn_free_handler_t free_handler;
    int64_t                     max_conns;
    my_slru_t                   *slru;
} my_http_client_session_pool_t;


static inline void
my_http_client_session_item_free(void *key, void *val)
{
    (void)val;
    my_http_client_conn_data_t *conn_data = key;
    my_http_client_conn_data_free(conn_data);
}


static inline void
my_http_client_session_pool_init(
    my_http_client_session_pool_t *pool,
    int64_t max_conns,
    my_http_conn_free_handler_t free_handler)
{
    if (max_conns <= 0) {
        max_conns = 1;
    }

    pool->max_conns = max_conns;
    pool->free_handler = free_handler;

    pool->slru = my_slru_create(
        MY_HTTP_CLIENT_SESSION_POOL_LRU_NLIST,
        max_conns, 0, NULL,
        my_http_client_session_item_free,
        my_http_client_conn_data_cmp);
}


// this function will copy key str.
void my_http_client_session_pool_put(my_str_t *key,
    my_conn_t *conn, int64_t wait_timeout);

my_conn_t *my_http_client_session_get(my_str_t *key);

// release all connections.
void my_http_client_session_pool_release();


